package com.engine.jucailinkq.attendance.workflow.action;

import com.engine.jucailinkq.attendance.attendanceanalysis.service.UtilService;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.impl.UtilServiceImpl;
import com.engine.jucailinkq.attendance.enums.AccountingUnitEnum;
import com.engine.jucailinkq.attendance.enums.AttendanceItemTypeEnum;
import com.engine.jucailinkq.attendance.enums.DateTypeEnum;
import com.engine.jucailinkq.attendance.enums.WorkForTimeEnum;
import com.engine.jucailinkq.attendance.workflow.service.MakeUpClockInService;
import com.engine.jucailinkq.attendance.workflow.service.impl.MakeUpClockInServiceImpl;
import com.engine.jucailinkq.common.util.CommonUtil;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.common.util.ServiceUtil;
import com.engine.jucailinkq.common.util.DbTools;
import com.engine.jucailinkq.common.util.Utils;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import weaver.general.TimeUtil;
import weaver.general.Util;
import weaver.interfaces.workflow.action.Action;
import weaver.soa.workflow.request.RequestInfo;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 加班计划流程提交检查
 */
@Slf4j
public class OvertimePlanCheckAction implements Action {

    private MakeUpClockInService makeUpClockInService = ServiceUtil.getService(MakeUpClockInServiceImpl.class);
    private UtilService utilService = ServiceUtil.getService(UtilServiceImpl.class);

    @Override
    public String execute(RequestInfo requestInfo) {
        String requestid = requestInfo.getRequestid();
        // 流程表单主表数据
        HashMap<String,String> mainTableData = CommonUtil.getMainTableInfo(requestInfo);
        // 流程表单明细表数据
        List<Map<String, String>> detailTableData = CommonUtil.getDetailTableInfo(requestInfo,0);
        log.info("OvertimePlanCheckAction_start, detailTableData_size : {}",detailTableData.size());
        //加班人员
        String jbry = mainTableData.get("jbry");
        try {
            //获取人员id和姓名信息
            List<String> empIdList = Arrays.asList(jbry.split(","));
            Map<String, String> empIdToNameInfo = CommonUtil.empIdToNameInfo(empIdList);
            //获取作用时段包含计划加班的加班类型的考勤项目集合
            String sql = "select id,mc, bddrqlx, jbqsfzs, xzzjbsc, rzdjbxss, zzdjbxss, yzdjbxss, zysd, ccclfs from uf_jcl_kq_kqxm where xmlx = ?";
            List<Map<String, Object>> jblxAttendanceList = DbTools.getSqlToList(sql, AttendanceItemTypeEnum.WORK_OVERTIME.getKey());
            Map<String, Map<String, Object>> jblxInfo = jblxAttendanceList.stream()
                    .filter(f -> Util.null2String(f.get("zysd")).contains(WorkForTimeEnum.PLAN_WORK_OVERTIME.getKey()))
                    .collect(Collectors.toMap(e->e.get("id").toString(), e->e));
            //明细数据按照人员分组
            Map<String,List<Map<String, String>>> detailGroupMap = detailTableData.stream().collect(Collectors.groupingBy(e->Util.null2String(e.get("jbry"))));
            List<String> errorMessage = new ArrayList<>();
            for (Map.Entry<String,List<Map<String, String>>> tableEntry :detailGroupMap.entrySet()){
                List<Map<String, String>> detailGroupByUserList = tableEntry.getValue();
                Map<String, Object> params = Maps.newHashMap();
                String empId = tableEntry.getKey();
                if ("".equals(empId)){
                    log.error("明细表中存在缺少加班人员信息的数据！");
                    requestInfo.getRequestManager().setMessageid("11111" + requestid + "22222");
                    requestInfo.getRequestManager().setMessagecontent("明细表中存在缺少加班人员信息的数据！");
                    return Action.FAILURE_AND_CONTINUE;
                }else {
                    params.put("userId", empId);
                }
                params.put("submitDate",DateUtil.getCurrentDate());
                params.put("submitStr","ksrq");
                params.put("submitDataList",detailGroupByUserList);

                /**
                 * 考勤周期校验
                 */
                if (detailGroupByUserList == null || detailGroupByUserList.size() == 0){
                    log.error("明细表没有数据！");
                    requestInfo.getRequestManager().setMessageid("11111" + requestid + "22222");
                    requestInfo.getRequestManager().setMessagecontent("明细表没有数据！");
                    return Action.FAILURE_AND_CONTINUE;
                }
                Map<String,Object> dataMap = makeUpClockInService.getKqCycleTimeIntervalCmd(params);
                List<Map<String,Object>> closeList = (List<Map<String,Object>>)dataMap.get("closeList");
                List<String> nocycleList = (List<String>)dataMap.get("nocycleList");
                List<Map<String,Object>> dateList = (List<Map<String,Object>>)dataMap.get("dataList");

                boolean status = (boolean)dataMap.get("status");
                if (!status){
                    log.error("该人员没有考勤周期");
                    requestInfo.getRequestManager().setMessageid("11111" + requestid + "22222");
                    requestInfo.getRequestManager().setMessagecontent("该人员没有考勤周期！");
                    return Action.FAILURE_AND_CONTINUE;
                }
                if (closeList.size() > 0 || nocycleList.size() > 0){
                    String message = "";
                    if (nocycleList.size() > 0){
                        message = message +String.join(",",nocycleList)+"未找对对应的考勤周期；";
                    }
                    if (closeList.size() > 0){
                        List<String> list = closeList.stream().map(e->e.get("rq").toString()).collect(Collectors.toList());
                        message = message +String.join(",",list)+"对应的考勤周期的考勤周期已关账";
                    }
                    requestInfo.getRequestManager().setMessageid("11111" + requestid + "22222");
                    requestInfo.getRequestManager().setMessagecontent(message);
                    return Action.FAILURE_AND_CONTINUE;
                }

                //校验加班类型中最小加班分钟数、工作日最大加班小时数、周最大加班小时数、月最大加班小时数等限定值
                List<Map<String, String>> overtimeDetailList = tableEntry.getValue();
                Map<String, Object> matchItemInfo;
                if (overtimeDetailList == null || overtimeDetailList.size() == 0) {
                    continue;
                }
                Map<String, Double> weekOvertimeInfo = new HashMap<>();
                Map<String, Double> monthOvertimeInfo = new HashMap<>();
                for (Map<String, String> overtimeDetailItem : overtimeDetailList) {
                    String belongDate = overtimeDetailItem.get("gsrq");
                    //从排班信息获取归属日日期类型
                    String belongDateType = utilService.getRqlxInScheduleInfo(empId, belongDate);
                    //排班信息无法获取日期类型时，从日历信息获取
                    belongDateType = "".equals(belongDateType) ? CommonUtil.getRqlx(empId, belongDate) : belongDateType;
                    //排班和日历都无法获取时，默认为工作日
                    belongDateType = "".equals(belongDateType) ? DateTypeEnum.WORK_DAY.getKey() : belongDateType;
                    String startDate = overtimeDetailItem.get("ksrq");
                    String jbsc = overtimeDetailItem.get("jbsc");
                    double overtimeMinutes = Double.parseDouble(jbsc) * 60;
                    String jblxId = overtimeDetailItem.getOrDefault("jblx", "");
                    matchItemInfo = jblxInfo.getOrDefault(jblxId, new HashMap<>());

                    String minMinutes = Util.null2String(matchItemInfo.get("jbqsfzs"));
                    if (!"".equals(minMinutes) && Integer.parseInt(minMinutes) > overtimeMinutes) {
                        //最小加班分钟数大于单条明细的加班时长分钟数
                        String message = Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + startDate + "的加班分钟数小于加班类型-"
                                + Util.null2String(matchItemInfo.get("mc")) +"设置的最小加班分钟数！";
                        log.error(message);
                        requestInfo.getRequestManager().setMessageid("11111" + requestid + "22222");
                        requestInfo.getRequestManager().setMessagecontent(message);
                        return Action.FAILURE_AND_CONTINUE;
                    }
                    String limitTotalOvertimeSc = Util.null2String(matchItemInfo.get("xzzjbsc"));
                    if ("1".equals(limitTotalOvertimeSc)) {
                        String limitWorkDayHours = Util.null2String(matchItemInfo.get("rzdjbxss"));
                        String limitWeekHours = Util.null2String(matchItemInfo.get("zzdjbxss"));
                        String limitMonthHours = Util.null2String(matchItemInfo.get("yzdjbxss"));
                        String limitDealType = Util.null2String(matchItemInfo.get("ccclfs"));
                        boolean doLimitWorkDayHours = false;
                        boolean doLimitWeekHours = false;
                        boolean doLimitMonthHours = false;
                        //判断是否满足工作日加班最大小时数
                        boolean needCheckWorkDayHours = !"".equals(limitWorkDayHours) && (belongDateType.equals(DateTypeEnum.WORK_DAY.getKey()) || belongDateType.equals(DateTypeEnum.CHANGECLASS.getKey()));
                        if (needCheckWorkDayHours && Double.compare(Double.parseDouble(limitWorkDayHours), overtimeMinutes / 60.0) < 0) {
                            doLimitWorkDayHours = true;
                            errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + startDate + "的加班时长累计后超过了加班类型-"
                                    + Util.null2String(matchItemInfo.get("mc")) +"设置的工作日加班最大小时数！");
                        }
                        //判断是否满足周加班最大小时数
                        if (!"".equals(limitWeekHours)) {
                            int weekRank = DateUtil.weekRank(belongDate);
                            double maxWeekMinutes = Double.parseDouble(limitWeekHours) *60;
                            double weekOvertimeMinutes;
                            if (weekOvertimeInfo.get(belongDate.split("-")[0] + "" + weekRank) == null) {
                                String countStartDate = DateUtil.beforeDay(belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-01", 6);
                                String countEndDate = DateUtil.AfterDay(belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-28", 9);
                                weekOvertimeMinutes = getWeekTimeMinutes(getWorkOverTimeResults(countStartDate, countEndDate, empId), belongDate);
                            } else {
                                weekOvertimeMinutes = weekOvertimeInfo.get(belongDate.split("-")[0] + "" + weekRank);
                            }
                            weekOvertimeInfo.put(belongDate.split("-")[0] + "" + weekRank, weekOvertimeMinutes + overtimeMinutes);
                            if (maxWeekMinutes - weekOvertimeMinutes - overtimeMinutes < 0) {
                                //达到周加班最大小时数
                                doLimitWeekHours = true;
                                errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + startDate + "的加班时长累计后超过了加班类型-"
                                        + Util.null2String(matchItemInfo.get("mc")) +"设置的周加班最大小时数！");
                            }
                        }
                        //判断是否满足月加班最大小时数
                        if (!"".equals(limitMonthHours)) {
                            String yearMonth = belongDate.substring(0, 7);
                            double maxMonthMinutes = Double.parseDouble(limitMonthHours) *60;
                            double monthOvertimeMinutes;
                            if (monthOvertimeInfo.get(yearMonth) == null) {
                                String countStartDate = belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-01";
                                String countEndDate = belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-31";
                                monthOvertimeMinutes = getMonthTimeMinutes(getWorkOverTimeResults(countStartDate, countEndDate, empId), belongDate);
                            } else {
                                monthOvertimeMinutes = monthOvertimeInfo.get(yearMonth);
                            }
                            monthOvertimeInfo.put(yearMonth, monthOvertimeMinutes + overtimeMinutes);
                            if (maxMonthMinutes - monthOvertimeMinutes - overtimeMinutes < 0) {
                                //达到月加班最大小时数
                                doLimitMonthHours = true;
                                errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + startDate + "的加班时长累计后超过了加班类型-"
                                        + Util.null2String(matchItemInfo.get("mc")) +"设置的月加班最大小时数！");
                            }
                        }
                            //判断是否超出工作日、周、月最大小时数要求，在加班类型考勤项目中设置为“禁止提交时”，返回报错
                            if ("1".equals(limitDealType) && (doLimitWorkDayHours || doLimitWeekHours || doLimitMonthHours)) {
                                log.error("超出加班类型工作日/周/月最大加班小时数限制！");
                                requestInfo.getRequestManager().setMessageid("11111" + requestid + "22222");
                                requestInfo.getRequestManager().setMessagecontent(String.valueOf(errorMessage));
                                return Action.FAILURE_AND_CONTINUE;
                            }
                    }
                }

            }

        }catch (Exception e){
            log.error("OvertimePlanCheckAction error : [{}]",e);
            return Action.FAILURE_AND_CONTINUE;
        }
        return Action.SUCCESS;
    }

    /**
     * 获得一周加班分钟数
     * @param dataList
     * @return
     */
    public double getWeekTimeMinutes(List<Map<String, Object>> dataList,String date){
        int day = TimeUtil.getDayOfWeek(date);
        if (day ==0){
            day = 7;
        }
        String startDate = DateUtil.beforeDay(date,day-1);
        String endDate = DateUtil.AfterDay(date,7-day);
        List<Map<String, Object>> list = dataList.stream().filter(e->{
            String sjksrq = Util.null2String(e.get("sjksrq"));
            if (DateUtil.getTime(sjksrq).compareTo(DateUtil.getTime(startDate)) >=0 &&
                    DateUtil.getTime(sjksrq).compareTo(DateUtil.getTime(endDate)) <=0  &&
                    DateUtil.getTime(sjksrq).compareTo(DateUtil.getTime(date)) !=0){
                return true;
            }else {
                return false;
            }
        }).collect(Collectors.toList());

        double totalMinutes = 0;
        for (Map<String, Object> data:list){
            String hsdw = data.get("hsdw").toString();
            totalMinutes += Utils.getItemduration(1, AccountingUnitEnum.MINUTES.getKey(), Double.valueOf(data.get("sjjbsc").toString()),AccountingUnitEnum.getEnum(hsdw),8);
        }
        return totalMinutes;
    }

    /**
     * 获得一个月加班分钟数
     * @param dataList
     * @return
     */
    public double getMonthTimeMinutes(List<Map<String, Object>> dataList,String date){
        String startDate = date.split("-")[0]+"-"+ date.split("-")[1]+"-01";
        String endDate = date.split("-")[0]+"-"+ date.split("-")[1]+"-31";
        List<Map<String, Object>> list = dataList.stream().filter(e->DateUtil.getTime(e.get("sjksrq").toString()).compareTo(DateUtil.getTime(startDate))>=0 &&
                DateUtil.getTime(e.get("sjjsrq").toString()).compareTo(DateUtil.getTime(endDate))<=0).collect(Collectors.toList());
        double totalMinutes = 0;
        for (Map<String, Object> data:list){
            String hsdw = data.get("hsdw").toString();
            totalMinutes += Utils.getItemduration(1,AccountingUnitEnum.MINUTES.getKey(), Double.valueOf(data.get("sjjbsc").toString()),AccountingUnitEnum.getEnum(hsdw),8);
        }

        return totalMinutes;
    }

    /**
     * 获取目标人员时间区间内的加班结果
     */
    public List<Map<String, Object>> getWorkOverTimeResults(String startDate,String endDate,String userId){
        String sql = "select a.sjjbsc, a.sjksrq, a.sjjsrq, a.jblx, b.hsdw from uf_jcl_kq_jbjg a left join uf_jcl_kq_kqxm b on a.jblx = b.id where a.jbry=? and a.sjksrq>=? and a.sjjsrq<=?";
        List<Map<String, Object>> dataList = DbTools.getSqlToList(sql,userId,startDate,endDate);
        return dataList;
    }
}
